#ifndef __MY_APP_H__
#define __MY_APP_H__

#include <wx/wx.h>
#include <osip2/osip.h>
#include "OsipProcess.h"
#include "ScenariosForm.h"
#include "wx/debugrpt.h"
#include "wx/ipc.h"
#include <wx/snglinst.h>


#if !defined(new) && defined(WXDEBUG_NEW) && wxUSE_MEMORY_TRACING && wxUSE_GLOBAL_MEMORY_OPERATORS
#define new WXDEBUG_NEW
#endif

#define STT_LStr(x) wxString((x), *wxConvCurrent).c_str()

#define STT_TRACE(t, l, wxV)  wxGetApp().Trace((t),(l),wxString::Format(wxT("%s %d %s"),STT_LStr(__FILE__),__LINE__,(wxString(wxV)).c_str()))
#define TRACE(l, wxV)  wxGetApp().Trace((l),wxString::Format(wxT("%s %d %s"),STT_LStr(__FILE__),__LINE__,(wxString(wxV)).c_str()))

#define STATUS_BAR_TIMER_ID 1000

enum{
    STT_MSG_INCOMMING,
    STT_MSG_OUTGOING,
};
int GetUidFromSipMsg(osip_message_t *pomt, int MsgType);

enum
{
    Minimal_Quit = 1,
    Minimal_About = wxID_ABOUT,
    Minimal_Conf,
    Minimal_Close,
    Minimal_Round_Test,
    Minimal_Random_Test,
    Minimal_CHANGE_IDENT,
    Minimal_Clear_Trace,
    Minimal_Clear_Statistic,
    Minimal_Clear_Timer,
    Minimal_Clear_Transaction,
    Minimal_Force_Port,
    Minimal_Update_AllCase,
    Minimal_GLOBAL_TRACE_SET,
    Minimal_GLOBAL_TRACE_INFO,
    Minimal_GLOBAL_TRACE_SIP,
    Minimal_GLOBAL_THROW_JUNK_RESPONSE,
    Minimal_GLOBAL_SHOW_REGSTATE,
	Minimal_GLOBAL_AUTO_100,
	Minimal_GLOBAL_LANGUAGE_CHOOSE_ENGLISH,
	Minimal_GLOBAL_LANGUAGE_CHOOSE_CHINESE,
};

enum
{
    SF_TERMNUM = 300,
    CMENU_DO_START,
    CMENU_DO_STOP,
    CMENU_START_TRACE,
    CMENU_CONF_SCEN,
    CMENU_CASE_STYLE,
    CMENU_DO_REPORT,
};

class OsipPollThread: public wxThread
{
public:
    OsipPollThread(osip_t *sip):wxThread(wxTHREAD_JOINABLE){osip=sip; Create();};
protected:
    virtual void *Entry();
    osip_t *osip;
};

class TimerStru :public wxObject
{
    DECLARE_CLASS(TimerStru);
 public:
    int TimerLen;
    wxDateTime StartAt;
    long ident;
};

class UidwxStringPair :public wxObject
{
    DECLARE_CLASS(UidwxStringPair);
 public:
    int Uid;
    wxString name;
    UidwxStringPair(int u, const wxString&n){Uid=u; name=n;};
    wxString ToString(){ return wxString::Format(wxT("%d|%s"),Uid,name.c_str());}
};

struct ltPair
{
  bool operator()(const UidwxStringPair s1, const UidwxStringPair s2) const
  {
    if (s1.Uid<s2.Uid) return true;
    if (s1.Uid>s2.Uid) return false;
    return s1.name<s2.name;
  }
};

class wxMyHash :public wxStringHash,wxObject
{
public:
    wxMyHash() {}
    unsigned long operator()( const UidwxStringPair& x ) const
    { wxString v=x.name+wxString::Format(wxT("%d"),x.Uid);  return wxCharStringHash( v.c_str() ); }
    wxMyHash& operator=(const wxMyHash&) { return *this; }
};


class wxMyEqual:public wxObject
{
    DECLARE_CLASS(wxMyEqual);
public:
    wxMyEqual() {}
    bool operator()( const UidwxStringPair& a, const UidwxStringPair& b ) const
    { return (a.Uid == b.Uid) && (a.name == b.name); }
    wxMyEqual& operator=(const wxMyEqual&) { return *this; }
};


WX_DECLARE_STRING_HASH_MAP(TimerStru, TimerCbHash);


/**
 * @defgroup TiemrModule 定时器模块

在场景执行过程中,每一个终端都有可能通过StartTimer行为申请一个或多个不同类型的定时器
定时器模块用来管理所有这些动态申请的定时器.

定时器模块为一个单独的线程,维护一个<PRE>std::map<UidwxStringPair,TimerStru,ltPair></PRE>结构的字典
对于定时器的申请请求,首先检查是否有相同终端已经申请了相同名称的定时器,这将导致申请失败,否则申请成功.

定时器线程则不断的循环检查这个字典列表,找到已经超时的定时器,将其从队列中释放,然后给相应的终端发送定时器超时事件

\sa Action_StartTimer
\sa TimerThread

*/
class TimerThread: public wxThread
{
public:
 TimerThread():wxThread(wxTHREAD_JOINABLE){Create(); Run();};
    bool RequestTimer(int Uid,int TimerLen,wxString name, long id);
    bool CancelTimer(int Uid, wxString name);
    bool CancelTimer(wxString timer);
    void RemoveTimer(wxString timer);
    size_t TimerNum(){ return AllTimerCb.size();};
    void DumpTimer(int Uid);
    bool CancelScenarioTimer(int ScenarioFormIndex);
protected:
    virtual void *Entry();
    TimerCbHash AllTimerCb;

};

enum
{
    MDI_QUIT = 100,
    MDI_NEW_WINDOW,
    MDI_REFRESH,
    MDI_CHANGE_TITLE,
    MDI_CHANGE_POSITION,
    MDI_CHANGE_SIZE,
    MDI_CHILD_QUIT,
    MDI_ABOUT
};

#define GLOBAL_KEY wxT("/Global")

WX_DECLARE_VOIDPTR_HASH_MAP(bool, TransValidHash);
class TransManager{
 public:
    bool NeedUpdate;
    wxMutex s_mutexProtectingTranses;

    void FreeDeadTrans();
    void ClearAllTrans();
    void AddTrans(osip_transaction_t *);
    void RemoveTrans(osip_transaction_t *, bool lock=true);
    void Erase(TransValidHash::iterator iter, bool lock=true);//{AllTranses.erase(iter);};
    bool IsExists(osip_transaction_t *t);
    bool IsAlive(osip_transaction_t *t);
    void DumpAll();
    TransValidHash::iterator GetBegin(){ return AllTranses.begin();};
    TransValidHash::iterator GetEnd(){ return AllTranses.end();};
 private:
    TransValidHash AllTranses;
};

WX_DEFINE_ARRAY(wxTextCtrl*,TxtCtrlArray);

class ConfigDialog : public wxDialog
{
public:
    ConfigDialog(wxWindow *parent, StrStrHash *amap=NULL);
    void OnButton(wxCommandEvent& event);

private:
    wxString Path;
    wxButton *m_btnFocused;
    wxArrayString keys;
    TxtCtrlArray values;
    StrStrHash *map;
    DECLARE_EVENT_TABLE()
};


enum{
    STT_MSG_TOTAL_SENT,
    STT_MSG_TOTAL_RECV,
    STT_OSIP_TRANS_TOTAL_FREE,
    STT_OSIP_CURRENT_TRANS_NUM,
    STT_STATISITEM_NUMS
};

class MyFrame : public wxMDIParentFrame
{
public:
    MyFrame();
    MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size,
            long style = wxDEFAULT_FRAME_STYLE);
    wxMenu *gtMenu;

    void InitToolBar(wxToolBar* toolBar);
    void OnQuit(wxCommandEvent& event);
    void OnClose(wxCloseEvent&);
    void OnClearTrace(wxCommandEvent& WXUNUSED(event));
    void OnClearTrans(wxCommandEvent& WXUNUSED(event));
    void OnClearStatictic(wxCommandEvent& WXUNUSED(event));
    void OnClearTimer(wxCommandEvent& WXUNUSED(event));
    void OnForcePort(wxCommandEvent& WXUNUSED(event));
    void OnUpdateAllCase(wxCommandEvent& WXUNUSED(event));
    void OnTimer(wxTimerEvent& event);
    //void OnIdle(wxIdleEvent& event);
	void OnLangEnglish(wxCommandEvent& WXUNUSED(event));
	void OnLangChinese(wxCommandEvent& WXUNUSED(event));
    void OnConfig(wxCommandEvent& WXUNUSED(event));
    void OnAbout(wxCommandEvent& event);
    void OnNewWindow(wxCommandEvent& event);
    void OnChangeIdent(wxCommandEvent& WXUNUSED(event));
    void StopTimer(){m_timer->Stop();};
private:
    wxTimer* m_timer;
    DECLARE_EVENT_TABLE()
};


class SipRawMsg:public wxObject{
    DECLARE_CLASS(SipRawMsg);
 public:
 SipRawMsg():wxObject(){ buf=NULL; len=0;};
 SipRawMsg(char*v, int l,sockaddr addr):wxObject(){
	buf=(char*)malloc(l+1);
	memcpy(buf,v,l);
	buf[l]=0;
	len=l;
	from = addr;
    };
    ~SipRawMsg(){ if (buf) free(buf), len=0;};
    char * buf;
    int len;
    sockaddr from;
};
WX_DEFINE_ARRAY(SipRawMsg *, SipRawEvtPtrArray);

//!  事件队列管理
/*!
    每一个场景执行拥有一个事件队列,管理新事件的压入和弹出
*/
class SipRawEventQueue:public wxObject{
    DECLARE_CLASS(SipRawEventQueue);
public:
    void push_back(SipRawMsg *e){
        wxMutexLocker lock(s_mutexProtectingTheQueue);
        q.push_back(e);
        };
    SipRawMsg *pop(){
        SipRawMsg *ret=NULL;
        wxMutexLocker lock(s_mutexProtectingTheQueue);
        if (q.size()>0){
            ret=*(q.begin());
            q.erase(q.begin());
        }
        return ret;
        };
    void clear(){
        wxMutexLocker lock(s_mutexProtectingTheQueue);
		SipRawEvtPtrArray::iterator iter;
		for (iter=q.begin();iter!=q.end();iter++){
			SipRawMsg*p = *iter;
			delete(p);
		}
	    q.clear();
        };
    size_t depth(){
        wxMutexLocker lock(s_mutexProtectingTheQueue);
        return q.size();
        };
private:
    wxMutex s_mutexProtectingTheQueue;
    SipRawEvtPtrArray q;
};

class stServer: public wxServer
{
 public:
    wxConnectionBase *OnAcceptConnection(const wxString& topic);
};

class stConnection : public wxConnection
{
public:
    stConnection() {}
    ~stConnection() {}
    bool OnExecute(const wxString& topic, wxChar*data, int size,
                   wxIPCFormat format);
};

class stClient: public wxClient
{
 public:
    stClient() {};
    wxConnectionBase *OnMakeConnection() { return new stConnection; }
};

class MyApp : public wxApp
{
    friend class MyFrame;
public:
    OsipPollThread *osipthread;
    OsipRecvThread *recvthread;
    TimerThread* timers;
    long AllStatistics[STT_STATISITEM_NUMS];
    SipRawEventQueue sipQueue;
    wxString ReportPath;
    ScenariosManager *m_ScenariosManager;
    osip_t *osip;
    bool ApplicationExit;
    TransManager AllTranses;

    MyFrame *frame;
    StrStrHash ConfigData;
    bool NoMd5;

    virtual bool OnInit();
    virtual int OnExit();

    bool MatchCallId(int Uid, std::string callidstr,bool AllowAppend=false);
    TermControl *GetTermIns(int Uid);

    void EraseFormIndex(ScenariosForm *form, int Index);
    wxString &GetHostName(){return HostName;};
    void Trace(int Level,const wxString& Value);
    void Trace(int Uid,int Level,const wxString& Value);
    void DisPatchEvt(int Uid, SttEvt*pstevt);
    void IncStatisticItem(int Item);
    bool RequestTimer(int Uid,int TimerLen,wxString name,long id){
	return timers->RequestTimer(Uid,TimerLen,name,id);
    }
    size_t TimerNum(){ return timers->TimerNum();};
    long MaxTimerNum;
    bool CancelTimer(int Uid, wxString name){
	return timers->CancelTimer(Uid,name);
    };
    bool CancelTimer(wxString timer){
	return timers->CancelTimer(timer);
    };
    void RemoveTimer(wxString timer){
		timers->RemoveTimer(timer);
    }
    bool NeedStopForMemNotEnough();
    void CheckAndPoll(bool HasTrans=true);
    void DumpTimer(int Uid){
	timers->DumpTimer(Uid);
    }

    bool CancelScenarioTimer(int ScenarioFormIndex){
      return timers->CancelScenarioTimer(ScenarioFormIndex);
    }

    int GetFormIndex(ScenariosForm *);
    void DoLog(const wxString& s);
    long GetTotalTransInStack();
    void StepSplash(const wxString& info);
    bool MyselfTooBusy();

    virtual void OnFatalException();
    void GenerateReport(wxDebugReport::Context ctx);
    void StopAndExit();

private:
    ScenariosForm *AllScenariosForm[MAX_SCENARIOS_NUM];
    wxLocale m_locale;
    wxString HostName;
    stServer *m_server;
    TraceForm *m_traceForm;
    long FreeMemWhenInit;
    long MaxTransNumInStack;
    void InitGlobalConfig();
    wxSingleInstanceChecker* m_checker;
};


DECLARE_APP(MyApp)

#endif
